package Simulation;

import java.math.BigDecimal;
import java.util.Vector;

import fxana.DateCell;
import fxana.FxRawDataSet;
import fxana.Range;
import fxana.RawDataCell;

enum ConditionState{
	NULL,
	WAITBUY,
	WAITSELL,
	SETTLED,
	LOSSBUY,
	LOSSSELL,
	BAD
}

public abstract class Condition {
	protected DateCell	BuildDate=null;
	protected DateCell	SetDate=null;
	protected DateCell	SettleDate=null;
	protected ConditionState	State=ConditionState.NULL;
	protected String		Type=null;
	
	Double SellValue=null;
	Double BuyValue=null;
	Double LossCutValue=0.0;
	Double tips=0.02;
	
	public Condition(){
		return;
	}
	public  Condition(FxRawDataSet ds, RawDataCell today,EvaluatorParaFlag flag){
		this.SetBuildDate(today);
		this.tips=0.02;
		
		Vector<Range>tmp=flag.GetRangeSet();
		if(!tmp.isEmpty()){
			int i=0;
			for(i=0;i<tmp.size();i++){
				if(tmp.elementAt(i).GetType()==Range.TYPE_DATE){
					if(!tmp.elementAt(i).InRange(today.GetRecDate())){
						this.State=ConditionState.BAD;
						return;
					}
				}
			}
		}
		
		PreProcess(ds,today,flag);
		
		RawDataCell est=this.GetEstimatedValue(ds, today,flag);
		if(est==null){
			this.State=ConditionState.BAD;
			return;
		}else{
			this.SellValue=this.Roundup(est.GetHighValue(), 2);
			this.BuyValue=this.Roundup(est.GetLowValue(), 2);
		}
		
		this.InitializeCondition(today);
		this.UpdateLossCutValue(ds, today, flag);
		this.UpdateCondition(ds, today,flag);
	}
	
	/* Preset some parameter like losscut, tips etc */
	abstract protected void PreProcess(FxRawDataSet ds, RawDataCell today,EvaluatorParaFlag flag);
	abstract protected Condition GetNew(FxRawDataSet ds, RawDataCell today,EvaluatorParaFlag flag);
	/* Define the method to estimate sell value and buy value */
	abstract protected RawDataCell GetEstimatedValue(FxRawDataSet ds, RawDataCell today,EvaluatorParaFlag flag);
	abstract protected void UpdateLossCutValue(FxRawDataSet ds, RawDataCell today,EvaluatorParaFlag flag);
	
	protected void InitializeCondition(RawDataCell today){
		if(State==ConditionState.NULL){
			if(today.GetHighValue().compareTo(SellValue)>=0){
				State=ConditionState.WAITBUY;
				SetSetDate(today);
				this.SetType("S->B");
				this.LossCutValue=this.Roundup(SellValue+this.LossCutValue,2);
			}else if(today.GetLowValue().compareTo(BuyValue)<=0){
				State=ConditionState.WAITSELL;
				SetSetDate(today);
				this.SetType("B->S");
				this.LossCutValue=this.Roundup(BuyValue-this.LossCutValue,2);
			}
		}
	}
	
	protected void SetBuildDate(DateCell date){	this.BuildDate=date;	}
	protected void SetSetDate(DateCell date){	this.SetDate=date;	}
	protected void SetSettleDate(DateCell date){	this.SettleDate=date;	}
	public DateCell GetBuildDate(){ return	this.BuildDate;	}
	public DateCell GetSetDate(){	return this.SetDate;	}
	public DateCell GetSettleDate(){	return SettleDate;	}

	public void SetType(String type) { this.Type=type;}
	public String GetType(){ return this.Type;}
	
	public void UpdateCondition(FxRawDataSet ds, RawDataCell today,EvaluatorParaFlag flag){
		this.UpdateLossCutValue(ds, today, flag);
		
		Vector<Range>tmp=flag.GetRangeSet();
		if(!tmp.isEmpty()){
			int i=0;
			for(i=0;i<tmp.size();i++){
				if(tmp.elementAt(i).GetType()==Range.TYPE_DATE){
					if(!tmp.elementAt(i).InRange(today.GetRecDate())){
						return;
					}
				}
			}
		}
		
		if(this.State==ConditionState.WAITBUY){
			if(today.GetHighValue().compareTo(LossCutValue)>=0){
				this.State=ConditionState.LOSSSELL;
				SetSettleDate(today);
				return;
			}else{
				RawDataCell est=this.GetEstimatedValue(ds, today,flag);
				if(est==null){
					return;
				}
				this.BuyValue=this.Roundup(est.GetLowValue(),2);
				if (today.GetLowValue().compareTo(BuyValue)<=0){
					this.State=ConditionState.SETTLED;
					SetSettleDate(today);
					return;
				}
			}
		}
		
		if(this.State==ConditionState.WAITSELL){
			if(today.GetLowValue().compareTo(LossCutValue)<=0){
				this.State=ConditionState.LOSSBUY;
				SetSettleDate(today);
				return;
			}else{
				RawDataCell est=this.GetEstimatedValue(ds, today,flag);
				if(est==null){
					return;
				}
				this.SellValue=this.Roundup(est.GetHighValue(),2);
				if (today.GetHighValue().compareTo(SellValue)>=0){
					this.State=ConditionState.SETTLED;
					SetSettleDate(today);
					return;
				}
			}
		}
		
		return;
	}
	
	public ConditionState GetState(){
		return this.State;
	}
	
	public Double GetMerit(){
		if(State==ConditionState.SETTLED)
			return (SellValue-BuyValue-this.tips)*10000;
		
		if(State==ConditionState.LOSSBUY)
			return (this.LossCutValue-BuyValue)*10000;
		
		if(State==ConditionState.LOSSSELL)
			return (this.SellValue-this.LossCutValue)*10000;
		
		return null;
	}
	
	public boolean IsWin(){
		return (this.GetMerit()>0.0);
	}
	
	public Double Roundup(Double x,Integer bit){
		BigDecimal bi=new BigDecimal(x);
		return bi.setScale(bit, BigDecimal.ROUND_HALF_UP).doubleValue();
	}
	
	public void Output(Integer title){
		if(title==0){
			System.out.printf("%-12s %-7s %-7s %-7s  %-11s %-13s %-12s %-7s \n", 
					"Date",
					"Buy",
					"Sell",
					"Loscutv",
					"SetDate",
					"SettleDate",
					"Type",
					"Merit");
			System.out.println("===================================================================================================");
		}else if (title==1){
			if(this.State==ConditionState.SETTLED || 
			   this.State==ConditionState.LOSSBUY ||
			   this.State==ConditionState.LOSSSELL	){
				System.out.printf("%-7s %-7.2f %-7.2f %-7.2f  %-7s %-7s %-7s %10.2f \n", this.BuildDate.GetFSRecDate(),
					this.BuyValue,
					this.SellValue,
					this.LossCutValue,
					this.SetDate.GetFSRecDate(),
					this.SettleDate.GetFSRecDate(),
					this.Type,
					this.GetMerit());
			}else{
				System.out.printf("%-7s %-7.2f %-7.2f %-7.2f  %-7s %-10s %6s %10s \n", this.BuildDate.GetFSRecDate(),
						this.BuyValue,
						this.SellValue,
						this.LossCutValue,
						this.SetDate.GetFSRecDate(),
						"Waiting",
						this.Type,
						"-");
			}
		}else if(title==2){
			System.out.println("===================================================================================================");
			System.out.printf("%-12s %-7s %-7s %-7s  %-11s %-13s %-12s %-7s \n", 
					"Date",
					"Buy",
					"Sell",
					"Loscutv",
					"SetDate",
					"SettleDate",
					"Type",
					"Merit");
		}else{
			int i;
			for(i=0;i<3;i++){
				this.Output(i);
			}
		}
	}
}